/**
 * Collaborative Story Development Web Application (CSDApp) Copyright Anton
 * Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The controller class handles user initiated requests for the particular
 * resource and prepares the correct view.
 *
 * @author Anton Strack
 */
public class PlotController extends PostController {

    private final String VIEW_LIST = "/plot/index.jsp";
    private final String VIEW_VIEW = "/plot/view.jsp";
    private final String VIEW_CREATE = "/plot/create.jsp";
    private final String VIEW_UPDATE = "/plot/update.jsp";
    private final String VIEW_DELTE = "/plot/delete.jsp";
    private CategoryManager categoryManager;

    public PlotController(Connection conn) {
        super(conn);
        this.categoryManager = new CategoryManager(conn, InfoAggregator.getInstance().getResourceTypeId("plot"));//this.ResourceRequestDelegator.getResourceId("plot"));
    }

    /**
     * This method primarily handles form related requests.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handlePostRequest(CsdwaRequest request) {
        String form = request.getFormId();
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        String postId = request.getParameter("secondaryId");
        request.setPrimaryResourceId(postId);
        int id = request.getPrimaryResourceId();
        if (form.isEmpty() == false) {
            request.setPrimaryResourceId(request.getParameter("postId"));
            if (form.equalsIgnoreCase("create-plot")) {
                request.removeFormId();
                request = handleCreate(request);
            } else if (form.equalsIgnoreCase("update-plot")) {
                request.removeFormId();
                request = handleUpdate(request);
            } else if (form.equalsIgnoreCase("delete-plot")) {
                request.removeFormId();
                request = handleDelete(request);
            }
        }
        return request;
    }

    /**
     * This method handles get requests. It accepts the primaryResouce and
     * primaryAction as variables to allow routing from the handleGetRequest
     * method.
     *
     * @param request the request object
     * @return an updated request object sufficient to complete the request.
     */
    @Override
    public CsdwaRequest handleGetRequest(CsdwaRequest request) {
        String primaryAction = request.getPrimaryActionTypeName();
        String primaryResource = request.getPrimaryResourceTypeName();
        String query = request.getParameter("query");
        int next = 0;
        int previous = 0;
        if (request.getParameter("next") != null && request.getParameter("next").matches(NUMBER_PATTERN)) {
            next = Integer.parseInt(request.getParameter("next"));
        } else if (request.getParameter("previous") != null && request.getParameter("previous").matches(NUMBER_PATTERN)) {
            previous = Integer.parseInt(request.getParameter("previous"));
        }
        int id = request.getPrimaryResourceId();
        if ("plot".equalsIgnoreCase(primaryResource)) {
            if ("view".equalsIgnoreCase(primaryAction) && id != 0) {
                PlotDao pdao = new PlotDao(conn);
                pdao.setUseRelatedMarkers(true);
                int result = pdao.selectBeanById(id);
                if (result > 0) {
                    CommentDao cdao = new CommentDao(conn);
                    cdao.setUseRelatedMarkers(true);
                    cdao.selectBeansByParent(id, pdao.getBean().resourceTypeId);
                    request.setBean(pdao.getBean());
                    request.setAttribute("comments", cdao.getBeans());
                    this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), InfoAggregator.getInstance().getActionTypeId("view"), pdao.getBean());
                    request.setView(VIEW_VIEW);
                    request = this.categoryManager.attachPostCategories(request);
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("characterinplot"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plothierarchy"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plotdialog"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plotlocation"));
                } else {
                    request.setMessage("The plot couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("plot");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } else if ("update".equalsIgnoreCase(primaryAction) && id != 0) {
                PlotDao pdao = new PlotDao(conn);
                int result = pdao.selectBeanById(id);
                if (result > 0) {
                    /* Store the bean in a session so that the it is available to the jsp page and also post update handler on post submit. This way it can compare the post bean values to the original bean values. Doing so will mean that only the columns changed will be sent to the database to be updated.
                     */
                    request.removeSessionAttribute("bean");
                    request.setSessionAttribute("bean", pdao.getBeans().get(0));
                    request.setView(VIEW_UPDATE);
                    request = this.categoryManager.attachPostCategories(request);
                } else {
                    request.setMessage("The plot couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("plot");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);

                }
            } else if ("create".equalsIgnoreCase(primaryAction)) {
                StateDao sDao = new StateDao(conn);
                sDao.selectStateByResourceTypeId(InfoAggregator.getInstance().getResourceTypeId("fixedstate"));
                request.setAttribute("stateIds", sDao.getBeans());
                request.setView(VIEW_CREATE);
            } else if ("delete".equalsIgnoreCase(primaryAction)) {
                Connection conn = this.conn;
                PlotDao pdao = new PlotDao(conn);
                int result = pdao.selectBeanById(id);
                if (result > 0) {
                    request.setBean(pdao.getBean());
                    CommentDao cdao = new CommentDao(conn);
                    cdao.selectBeansByParent(id, pdao.getBean().resourceTypeId);
                    List<Comment> comments = cdao.getBeans();
                    if (comments.isEmpty() == false) {
                        request.setAttribute("comments", comments);
                    }
                    request.setView(VIEW_DELTE);
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("characterinplot"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plothierarchy"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plotdialog"));
                    request = this.getResourceRequestDelegator().getRelationshipManager().attachPostMappingsToPost(request, InfoAggregator.getInstance().getResourceTypeId("plotlocation"));
                    request = this.categoryManager.attachPostCategories(request);
                } else {
                    request.setMessage("The plot couldn't be found.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("list");
                    request.setPrimaryResourceTypeName("plot");
                    request.setPrimaryResourceId(0);
                    request = handleGetRequest(request);
                }
            } else if ("search".equalsIgnoreCase(primaryAction) && query != null && query.isEmpty() == false) {
            } else if ("list".equalsIgnoreCase(primaryAction) && next > 0 && id > 0) {
                CsdwaDao<Plot> pdao = new PlotDao(conn);
                Paginator<Plot> paginator = new Paginator<Plot>(next);
                paginator.setNextId(id);
                pdao = paginator.getNext(pdao);
                if (pdao.getBeans().size() <= 0) {
                    request.setMessage("There are no plots to list.");
                    request.setMessageType("info");
                }
                request.setAttribute("beans", pdao.getBeans());
                request.setView(VIEW_LIST);
                request.setAttribute("paginator", paginator);
            } else if ("list".equalsIgnoreCase(primaryAction) && previous > 0 && id > 0) {
                CsdwaDao<Plot> pdao = new PlotDao(conn);
                Paginator<Plot> paginator = new Paginator<Plot>(previous);
                paginator.setPreviousId(id);
                pdao = paginator.getPrevious(pdao);
                if (pdao.getBeans().size() <= 0) {
                    request.setMessage("There are no plots to list.");
                    request.setMessageType("info");
                }
                request.setAttribute("beans", pdao.getBeans());
                request.setView(VIEW_LIST);
                request.setAttribute("paginator", paginator);
            } else {// index by default
                CsdwaDao<Plot> pdao = new PlotDao(conn);
                Paginator<Plot> paginator = new Paginator<Plot>(10); // default page size
                pdao = paginator.getNext(pdao);
                if (pdao.getBeans().size() <= 0) {
                    request.setMessage("There are no plots to list.");
                    request.setMessageType("info");
                }
                request.setAttribute("beans", pdao.getBeans());
                request.setView(VIEW_LIST);
                request.setAttribute("paginator", paginator);
            }
        }
        return request;
    }

    /**
     * This method saves the bean to the database.
     *
     * @param request the create request object containing the form values.
     * @return the request object with the requestResultMessage object set to
     * indicate the result
     */
    private CsdwaRequest handleCreate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        // Create validators to use for validation
        CsdwaValidator isNotEmpty = new IsNotEmpty();
        CsdwaValidator isNumeric = new IsNumeric();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("postName"), "Name");
        ValidatorValue description = new ValidatorValue(request.getParameter("postDescription"), "Description");
        ValidatorValue body = new ValidatorValue(request.getParameter("postBody"), "Body");
        ValidatorValue state = new ValidatorValue(request.getParameter("postState"), "State");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        description.addValidator(isNotEmpty);
        body.addValidator(isNotEmpty);
        state.addValidator(isNumeric).addValidator(isNotEmpty);
        // isValid the values.
        if (name.isValid() && description.isValid() && body.isValid() && state.isValid()) {
            Plot post = ResourceFactory.makePlot();
            post.setName(name.getString());
            post.setDescription(description.getString());
            post.setBody(body.getString());
            post.setStateId(state.getInteger());
            post.setParentId(0);// no parent since this is new.
            post.setUpdatedById(user.getId());
            post.setCreatedById(user.getId());
            post.setCreatedDate(timestamp);
            post.setUpdatedDate(timestamp);

            PlotDao pdao = new PlotDao(conn);
            int result = pdao.insertBean(post);
            if (result >= 0) {
                post.setId(result);
                // success so go to view page of newly created plot
                request.setMessage("The plot was successfully created.");
                request.setMessageType("success");
                this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), InfoAggregator.getInstance().getActionTypeId("create"), post);
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName("plot");
                request.setPrimaryResourceId(result);
                request = handleGetRequest(request);
            } else {
                request.setMessage("There was an error saving the plot to the database.");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("create");
                request.setPrimaryResourceTypeName("plot");
                request.setPrimaryResourceId(0);
                request = handleGetRequest(request);
            }

        } else {
            // If there is an error, one of these validatorValues will contain the message indicating the error, the others will return empty strings.
            request.setMessage(name.getErrorMessage() + description.getErrorMessage() + body.getErrorMessage() + state.getErrorMessage());
            request.setMessageType("info");
            request.setPrimaryActionTypeName("create");
            request.setPrimaryResourceTypeName("plot");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }

        return request;
    }

    /**
     * This method handles updating the bean. If the bean to be updated was
     * properly set as a bean in the session, then this method will instruct the
     * dao to update only the fields that were changed and thus reduce message
     * size.
     *
     * @param request the update request object
     * @return a request object with the requestResultMessage object set to
     * indicate the update result.
     */
    private CsdwaRequest handleUpdate(CsdwaRequest request) {
        User user = (User) request.getSessionAttribute("user");
        Timestamp timestamp = new Timestamp(new Date().getTime());
        // isValid fields
        CsdwaValidator isNotEmpty = new IsNotEmpty();
        // add request parameter values to the object to test validation
        ValidatorValue name = new ValidatorValue(request.getParameter("postName"), "Name");
        ValidatorValue description = new ValidatorValue(request.getParameter("postDescription"), "Description");
        ValidatorValue body = new ValidatorValue(request.getParameter("postBody"), "Body");
        // the the validators to the validation values
        name.addValidator(isNotEmpty);
        description.addValidator(isNotEmpty);
        body.addValidator(isNotEmpty);

        Plot originalPost = (Plot) request.getSessionAttribute("bean");

        // isValid the values.
        if (name.isValid() && description.isValid() && body.isValid()) {
            PlotDao pdao = new PlotDao(conn);
            StringBuilder sb = new StringBuilder();
            if (originalPost == null) {
                originalPost = ResourceFactory.makePlot();// if the original post didn't get stored in the session, then create a new one.
            }

            if (originalPost.getName().equals(name.getString()) == false) {
                sb.append("Name");
            }
            if (originalPost.getDescription().equals(description.getString()) == false) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("Description");
            }
            if (originalPost.getBody().equals(body.getString()) == false) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("Body");
            }
            /*
             if (originalPost.getStateId() != Integer.parseInt(postState)) {
             if (sb.length() > 0) {
             sb.append(", ");
             }
             sb.append("StateID");
             }

             * */
            // always set updated by id
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append("UpdatedByID");
            // always set updated date
            if (sb.length() > 0) {
                sb.append(", ");
            }
            sb.append("UpdatedDate");
            int originalPostState = originalPost.getStateId();// need to keep track of the original post state for the update
            // save old version of post as a new post with the prior version state
            originalPost.setStateId(InfoAggregator.getInstance().getStateTypeId("priorversion"));
            int parentId = pdao.insertBean(originalPost);
            if (parentId > 0) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append("ParentID");
            }

            // update the original bean with all the values from the form submit
            originalPost.setName(name.getString());
            originalPost.setDescription(description.getString());
            originalPost.setBody(body.getString());
            originalPost.setStateId(originalPostState);
            originalPost.setParentId(parentId);
            originalPost.setUpdatedById(user.getId());
            originalPost.setUpdatedDate(timestamp);

            // declare the columns to update.
            pdao.setColumnsToUpdate(sb.toString());

            int result = pdao.updateBean(originalPost);
            request.removeSessionAttribute("bean");
            if (result >= 0) {
                this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), InfoAggregator.getInstance().getActionTypeId("update"), originalPost);
                // success so go to view page of updated plot
                request.setMessage("The plot was successfully updated.");
                request.setMessageType("success");
                request.setPrimaryActionTypeName("view");
                request.setPrimaryResourceTypeName("plot");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            } else {
                request.setMessage("There was an error trying to update the plot to the database.");
                request.setMessageType("error");
                request.setPrimaryActionTypeName("update");
                request.setPrimaryResourceTypeName("plot");
                request.setPrimaryResourceId(originalPost.getId());
                request = handleGetRequest(request);
            }

        } else {
            // If there is an error, one of these validatorValues will contain the message indicating the error, the others will return empty strings.
            request.setMessage(name.getErrorMessage() + description.getErrorMessage() + body.getErrorMessage());
            request.setMessageType("error");
            request.setPrimaryActionTypeName("update");
            request.setPrimaryResourceTypeName("plot");
            request.setPrimaryResourceId(originalPost.getId());
            request = handleGetRequest(request);
        }

        return request;
    }

    /**
     * This method handles deleting the specific bean from the database. This
     * method handles the cascading deletes of all dependant resources.
     *
     * @param request the delete request object.
     * @return a request object with a requestResultMessage object specifying
     * the result.
     */
    private CsdwaRequest handleDelete(CsdwaRequest request) {
        // validate fields
        int id = request.getPrimaryResourceId();
        boolean allowDelete = false;
        if (id > 0) {
            try {
                // set auto commit off for connection
                Connection conn = this.conn;
                conn.setAutoCommit(false);// enter transaction mode
                Savepoint delete = conn.setSavepoint("delete-plot_" + id);
                PlotDao pdao = new PlotDao(conn);
                if (pdao.selectBeanById(id) > 0) {
                    allowDelete = true;
                }
                if (allowDelete == true) {
                    Plot post = pdao.getBean();
                    int parentId = post.getParentId();
                    pdao.clearDaoBeans();
                    pdao.makeParentChain(parentId);
                    allowDelete = this.deleteDependants(pdao);
                    CommentDao cdao = new CommentDao(conn);
                    cdao.selectBeansByParent(id, post.getResourceTypeId());
                    if (allowDelete == true) {
                        allowDelete = this.deleteDependants(cdao);
                    }
                    if (allowDelete == true) {
                        RelatedMapperDao rmDao = this.ResourceRequestDelegator.getRelationshipManager().getRelatedMapperDao(InfoAggregator.getInstance().getResourceTypeId("plothierarchy"));
                        rmDao.setConn(conn);
                        rmDao.selectMappingsByPostAId(id);
                        allowDelete = this.deleteDependants(rmDao);
                    }

                    if (allowDelete == true) {
                        int result = pdao.deleteBean(post);
                        request.removeAttribute("formSubmit");
                        if (result > 0) {
                            // conn.commit();
                            this.getResourceRequestDelegator().getUserHistoryManager().recordUserPostHistory(request.getCurrentUser(), InfoAggregator.getInstance().getActionTypeId("delete"), post);
                            conn.setAutoCommit(true);// reset commit mode to auto and commit the transaction since the deletion worked correctly.
                            request.setMessage("The plot was successfully deleted from the site.");
                            request.setMessageType("success");
                            request.setPrimaryActionTypeName("list");
                            request.setPrimaryResourceTypeName("plot");
                            request.setPrimaryResourceId(0);
                            request = handleGetRequest(request);
                        } else {
                            conn.rollback(delete); // delete failed so roll back connection
                            conn.setAutoCommit(true);// reset commit mode to auto
                            request.setMessage("There was an error trying to delete the plot from the database.");
                            request.setMessageType("error");
                            request.setPrimaryActionTypeName("view");
                            request.setPrimaryResourceTypeName("plot");
                            request.setPrimaryResourceId(id);
                            request = handleGetRequest(request);
                        }
                    } else {
                        conn.rollback(delete); // delete failed so roll back connection
                        conn.setAutoCommit(true);// reset commit mode to auto
                        request.setMessage("A dependant resource could not be deleted. Deletion aborted.");
                        request.setMessageType("error");
                        request.setPrimaryActionTypeName("view");
                        request.setPrimaryResourceTypeName("plot");
                        request.setPrimaryResourceId(id);
                        request = handleGetRequest(request);
                    }
                } else {
                    conn.rollback(delete); // delete failed so roll back connection
                    conn.setAutoCommit(true);// reset commit mode to auto
                    request.setMessage("A dependant resource could not be deleted. Deletion aborted.");
                    request.setMessageType("error");
                    request.setPrimaryActionTypeName("view");
                    request.setPrimaryResourceTypeName("plot");
                    request.setPrimaryResourceId(id);
                    request = handleGetRequest(request);
                }

            } catch (SQLException ex) {
                Logger.getLogger(PlotController.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            request.setMessage("There was an error with the delete request. Could not determine the plot ID.");
            request.setMessageType("error");
            request.setPrimaryActionTypeName("list");
            request.setPrimaryResourceTypeName("plot");
            request.setPrimaryResourceId(0);
            request = handleGetRequest(request);
        }
        return request;
    }
}
